package test.base.medium;

import static org.bytedeco.ffmpeg.global.avcodec.av_packet_unref;

import java.io.IOException;

import org.bytedeco.ffmpeg.avcodec.AVPacket;
import org.bytedeco.ffmpeg.avformat.AVFormatContext;
import org.bytedeco.javacv.FFmpegFrameGrabber;
import org.bytedeco.javacv.FFmpegFrameRecorder;
import org.bytedeco.javacv.Frame;
import org.bytedeco.javacv.FrameGrabber.Exception;

import hyl.core.MyFun;
import test.base.medium.ffmpeg.FrameGrabber;

/**
 * rtsp转rtmp（转封装方式）
 * 
 * @author eguid
 */
public class javacvRtsptest1 {

	FFmpegFrameRecorder record = null;// Plus
	int width = -1, height = -1;

	// 视频参数
	protected int audiocodecid;
	protected int codecid;
	protected double framerate;// 帧率
	protected int bitrate;// 比特率

	// 音频参数
	// 想要录制音频，这三个参数必须有：audioChannels > 0 && audioBitrate > 0 && sampleRate > 0
	private int audioChannels;
	private int audioBitrate;
	private int sampleRate;
	int audioCodec;

	/**
	 * 转封装
	 * 
	 * @author eguid
	 * @throws IOException
	 */
	public static void to(String inputurl, String outurl) throws IOException {
		// 获取视频源
		FFmpegFrameGrabber grab = new FFmpegFrameGrabber(inputurl);
		grab.start();
		// 录制/推流器
		FFmpegFrameRecorder record = new FFmpegFrameRecorder(outurl, grab.getImageWidth(), grab.getImageHeight());
		// record.setVideoOption("crf", "18");
		// record.setGopSize(2);
		// record.setFrameRate(1);
		// record.setVideoBitrate(bitrate);

		// record.setAudioChannels(audioChannels);
		// record.setAudioBitrate(audioBitrate);
		// record.setSampleRate(sampleRate);
		AVFormatContext fc = grab.getFormatContext();
		// if (out.indexOf("rtmp") >= 0 || out.indexOf("flv") > 0) {
		// 封装格式flv
		record.setFormat("flv");
		// record.setAudioCodecName("aac");
		// record.setVideoCodec(codecid);
		// record.setAudioCodec(audioCodec);

		// }
		record.start(fc);
		long err_index = 0;// 采集或推流导致的错误次数
		// 连续五次没有采集到帧则认为视频采集结束，程序错误次数超过1次即中断程序
		for (int no_frame_index = 0; no_frame_index < 5 || err_index > 1;) {
			AVPacket pkt = null;
			// Frame pkt=null;
			try {
				// 没有解码的音视频帧
				pkt = grab.grabPacket();
				if (pkt == null || pkt.size() <= 0 || pkt.data() == null) {
					// 空包记录次数跳过
					no_frame_index++;
					continue;
				}
				// 不需要编码直接把音视频帧推出去
				err_index += (record.recordPacket(pkt) ? 0 : 1);// 如果失败err_index自增1
				System.out.println(err_index);
				// av_free_packet(pkt);
				av_packet_unref(pkt);
			} catch (Exception e) {// 推流失败
				err_index++;
			} catch (IOException e) {
				err_index++;
			}
		}
	
	}

	/**
	 * 按帧录制视频
	 * 
	 * @param            inputFile-该地址可以是网络直播/录播地址，也可以是远程/本地文件路径
	 * @param outputFile -该地址只能是文件地址，如果使用该方法推送流媒体服务器会报错，原因是没有设置编码格式
	 * @throws FrameGrabber.Exception
	 * @throws FrameRecorder.Exception
	 * @throws org.bytedeco.javacv.FrameRecorder.Exception
	 */
	public static void frameRecord(String inputFile, String outputFile, int audioChannel)
			throws Exception, org.bytedeco.javacv.FrameRecorder.Exception {

		boolean isStart = true;// 该变量建议设置为全局控制变量，用于控制录制结束
		// 获取视频源
		FFmpegFrameGrabber grabber = new FFmpegFrameGrabber(inputFile);
		// 流媒体输出地址，分辨率（长，高），是否录制音频（0:不录制/1:录制）
		FFmpegFrameRecorder recorder = new FFmpegFrameRecorder(outputFile, 640, 480, audioChannel);
		// 开始取视频源
		recordByFrame(grabber, recorder, isStart);
	}

	private static void recordByFrame(FFmpegFrameGrabber grabber, FFmpegFrameRecorder recorder, Boolean status)
			throws Exception, org.bytedeco.javacv.FrameRecorder.Exception {
		try {// 建议在线程中使用该方法
			grabber.setOption("rtsp_transport", "tcp");
			// grabber.setFrameRate(28) ;
			// recorder.setFrameRate(28) ;
			// recorder.setFormat(format);
			recorder.setFormat("flv");
			grabber.start();
			recorder.start();
			Frame frame = null;
			int i = 0;
			while (status && (frame = grabber.grabFrame()) != null) {
				recorder.record(frame);
				if (i++ == 100)
					break;

			}
			recorder.stop();
			grabber.stop();
		} finally {
			if (grabber != null) {
				grabber.stop();
			}
		}
	}

	public static void main(String[] args) throws Exception, IOException {
		String rtspPath = "rtsp://admin:nt123321@192.168.0.2:554/h264/1/sub/av_stream";
		String rtmpPath = "rtmp://192.168.0.147:1935/hly/zdy1";
		String tagfile = "d:\\test\\mov\\1.flv";
//运行，设置视频源和推流地址
		to(rtspPath,tagfile);
		//frameRecord(rtspPath, tagfile, 1);
	}

	// 抓帧器
	FFmpegFrameGrabber grabber;
	// 录像机
	FFmpegFrameRecorder recorder;

	/**
	 * 每秒播发多少张
	 * 
	 * @param 帧数
	 * @return
	 */

	public void create抓取器(String rtspUrl, String rtsp传输协议) throws Exception {
		// 采集/抓取器
		// 设置 每帧图像宽高

		// 获取视频源

		grabber = FFmpegFrameGrabber.createDefault(rtspUrl);
		// tcp用于解决丢包问题,如果不担心丢包可以用udp
		grabber.setOption("rtsp_transport", rtsp传输协议);
		// 设置采集器构造超时时间 设置超时断开连接时间，单位微秒
		// grabber.setOption("stimeout", "120000");
		// 设置缓存大小，1080p可将值调大
		// grabber.setOption("buffer_size", "500000");
		// 设置最大时延
		// grabber.setOption("max_delay", "60000");

		// 参数说明：
		// （1）ImageMode：用来选择像素模式，分别有三个常量：COLOR（颜色模式）,
		// GRAY（灰度图模式，出来的图像是灰度图，用于主要用于方便后期图像处理）, RAW（原生模式）
		// （2）pixelFormat、width、height和imageScalingFlags：这四个参数是会在像素格式转换中进行转换的内容。

		// 只有设置ImageMode.RAW的时候才不会进行格式转换，设置成color和gray都会触发像素格式转换，
		// 默认是ImageMode.COLOR模式
		// grabber.setImageMode(ImageMode.RAW);
		// grabber.setFormat(format);//这个参数设置是没有用的
		// grabber.setVideoCodec(avcodec.AV_CODEC_ID_NONE);//
		// 编码格式，用于编码和解码。使用avcodec中的编码常量，例如：avcodec.AV_CODEC_ID_NONE
		// 设置帧率
		// grabber.setFrameRate(in视频.i帧);

		// 如果不触发SwsContext像素格式转换，pixelFormat、width、height和imageScalingFlags参数不会起效果
		// 设置获取的视频宽度
		// grabber.setImageWidth(in视频.i宽);
		// 设置获取的视频高度
		// grabber.setImageHeight(in视频.i高);
		// grabber.setImageScalingFlags(0);// 缩放比例,0表示不缩放
		// grabber.setPixelFormat(avutil.AV_PIX_FMT_NONE);// 使用avutil中的像素格式常
		// 量，例如：avutil.AV_PIX_FMT_NONE
		grabber.start();// 开始之后ffmpeg会采集视频信息，之后就可以获取音视频信息
		// int width = grabber.getImageWidth();
		// int height = grabber.getImageHeight();
		// 若视频像素值为0，说明采集器构造超时，程序结束
//			if (width == 0 && height == 0) {
//				System.err.println(" 拉流超时...");
//				return null;
//			}
		// 视频参数
		// in视频.setI编码(grabber.getVideoCodec());
		// in视频.setI帧((int) grabber.getVideoFrameRate());
		// 设置视频bit率
		// in视频.setI分辨率(grabber.getVideoBitrate());

		// 音频参数
		// 想要录制音频，这三个参数必须有：audioChannels > 0 && audioBitrate > 0 && sampleRate > 0

		// 音频编码
		// in音频.i编码 = grabber.getAudioCodec();
		// in音频.i通道数 = grabber.getAudioChannels();
		// in音频.i码率 = grabber.getAudioBitrate();
		// Frame frame = grabber.grabImage();//包,帧内存会被下一帧覆盖掉,如果处理慢了会导致延迟

	}

	/**
	 * 创建输出器
	 * 
	 * @author zdy
	 * 
	 */
	public void createRtmp录制器(String 输出路径) throws IOException {
		createRtmp录制器(输出路径, null);
	}

	public void createRtmp录制器(String 输出路径, String 画面质量) throws IOException {

		if (MyFun.isEmpty(画面质量))
			画面质量 = "18";
		// out视频.ini(in视频);
		// out音频.ini(in音频);
		// 录制/推流器
		recorder = new FFmpegFrameRecorder(输出路径, grabber.getImageWidth(), grabber.getImageHeight());
		// 视频质量参数(详见 https://trac.ffmpeg.org/wiki/Encode/H.264)
		// recorder.setVideoOption("crf", 画面质量);// 画面质量参数，0~51；18~28是一个合理范围
		// 提升编码速度
		// recorder.setVideoOption("preset", "ultrafast");
		// 降低编码延时
		// recorder.setVideoOption("tune", "zerolatency");
		// recorder.setFrameRate(18);
		// recorder.setVideoCodec(out视频.i编码);
		recorder.setFormat("flv");// 这是javacv自己封装的一套转换机制，与ffmpeg不同，只支持flv，mp4，3gp和avi四种格式
		// recorder.setGopSize(2);// gop间隔
		// recorder.setFrameRate(out视频.i帧);
		// 分辨率
		// recorder.setVideoBitrate(out视频.i分辨率);
		// 设置像素格式 只有pixelFormat，width，height三个参数任意一个不为空才会进行像素格式转换
		// recorder.setPixelFormat(out视频.i像素格式);
		// imageScalingFlags（缩放比例）：进行像素格式转换的内容还是这四个：pixelFormat，width，height和imageScalingFlags。但是这个imageScalingFlags参数在FFmpegFrameRecorder中却没有提供设置参数
		// recorder.setVideoQuality(double 视频质量 );//视频质量
		// 音频参数
		// 想要录制音频，这三个参数必须有：audioChannels > 0 && audioBitrate > 0 && sampleRate > 0

		// record.setAudioCodecName("aac");
		// 强制声音编码
		// recorder.setAudioCodec(out音频.i编码);
		// recorder.setAudioChannels(out音频.i通道数);
		// 声音码率
		// recorder.setAudioBitrate(out音频.i码率);
		// 声音取样频率
		// recorder.setSampleRate(out音频.i采样频率);
		// AVFormatContext描述了一个媒体文件或媒体流的构成和基本信息，位于avformat.h文件中。
		AVFormatContext fc = grabber.getFormatContext();

		// fc.max_interleave_delta(100);
		// 基本不需要设置,所有设置在start()中已经实现
		// 通过这总方式开启时 基本不需要设置参数，因为所有参数与输入相同
		recorder.start(fc);

	}
}
